package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"github.com/blang/semver"
	"github.com/hound-search/hound/api"
	"github.com/hound-search/hound/config"
	mysql "github.com/hound-search/hound/dao"
	"github.com/hound-search/hound/searcher"
	"github.com/hound-search/hound/ui"
	"github.com/hound-search/hound/web"
	"github.com/robfig/cron"
	"log"
	"net/http"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"runtime"
	"strings"
	"syscall"
	"time"
)

const gracefulShutdownSignal = syscall.SIGTERM

var (
	info_log   *log.Logger
	error_log  *log.Logger
	_, b, _, _ = runtime.Caller(0)
	basepath   = filepath.Dir(b)
)

func makeSearchers(cfg *config.Config) (map[string]*searcher.Searcher, bool, error) {
	// Ensure we have a dbpath
	if _, err := os.Stat(cfg.DbPath); err != nil {
		if err := os.MkdirAll(cfg.DbPath, os.ModePerm); err != nil {
			return nil, false, err
		}
	}

	searchers, errs, err := searcher.MakeAll(cfg)
	if err != nil {
		return nil, false, err
	}

	if len(errs) > 0 {
		// NOTE: This mutates the original config so the repos
		// are not even seen by other code paths.
		for name, _ := range errs { //nolint
			delete(cfg.Repos, name)
		}

		return searchers, false, nil
	}

	return searchers, true, nil
}

func nowHandleShutdown(searchers map[string]*searcher.Searcher, cfg *config.Config) (idx map[string]*searcher.Searcher) {
	for _, s := range searchers {
		s.Stop()
	}

	for _, s := range searchers {
		s.Wait()
	}
	return getConfigFromMysql(cfg)
}

func handleShutdown(shutdownCh <-chan os.Signal, searchers map[string]*searcher.Searcher) {
	go func() {
		<-shutdownCh
		info_log.Printf("Graceful shutdown requested...")
		for _, s := range searchers {
			s.Stop()
		}

		for _, s := range searchers {
			s.Wait()
		}

		os.Exit(0)
	}()
}

func registerShutdownSignal() <-chan os.Signal {
	shutdownCh := make(chan os.Signal, 1)
	signal.Notify(shutdownCh, gracefulShutdownSignal)
	return shutdownCh
}

func makeTemplateData(cfg *config.Config) (interface{}, error) { //nolint
	var data struct {
		ReposAsJson string
	}

	res := map[string]*config.Repo{}
	for name, repo := range cfg.Repos {
		res[name] = repo
	}

	b, err := json.Marshal(res)
	if err != nil {
		return nil, err
	}

	data.ReposAsJson = string(b)
	return &data, nil
}

func runHttp( //nolint
	addr string,
	dev bool,
	cfg *config.Config,
	idx map[string]*searcher.Searcher) error {
	m := http.DefaultServeMux

	h, err := ui.Content(dev, cfg)
	if err != nil {
		return err
	}

	m.Handle("/", h)
	api.Setup(m, idx)
	return http.ListenAndServe(addr, m)
}

func getVersion() semver.Version {
	return semver.Version{
		Major: 0,
		Minor: 4,
		Patch: 0,
	}
}

type ChihiroGitlabDetail struct {
	ID             int64
	GroupID        int
	GitlabID       int
	GitlabName     string
	GitlabCloneUrl string
	CreateTime     time.Time
	UpdateTime     time.Time
}

var EnablePollUpdates = false

func getConfigFromMysql(cfg *config.Config) (idx map[string]*searcher.Searcher) {
	var g []ChihiroGitlabDetail
	var err error
	if err = mysql.DB.Find(&g).Error; err != nil {
		fmt.Printf("数据库查询报错：%v\n", err)
	}

	r := make(map[string]*config.Repo)
	for _, value := range g {
		r[value.GitlabName] = &config.Repo{
			Url:              fmt.Sprintf("http://gitlab2.bitautotech.com%s.git", value.GitlabCloneUrl),
			MsBetweenPolls:   3000,
			Vcs:              "git",
			VcsConfigMessage: nil,
			UrlPattern: &config.UrlPattern{
				BaseUrl: "{url}/blob/{rev}/{path}{anchor}",
				Anchor:  "#L{line}",
			},
			ExcludeDotFiles:   false,
			EnablePollUpdates: &EnablePollUpdates,
			EnablePushUpdates: nil,
		}
	}
	if err == nil {
		cfg.Repos = r
	}

	var ok bool
	idx, ok, err = makeSearchers(cfg)
	if err != nil {
		log.Panic(err)
	}
	if !ok {
		info_log.Println("部分仓库构建失败")
	} else {
		info_log.Println("所有索引构建完成")
	}
	return idx
}

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())
	info_log = log.New(os.Stdout, "", log.LstdFlags)
	error_log = log.New(os.Stderr, "", log.LstdFlags)

	flagConf := flag.String("conf", "config.json", "")
	flagAddr := flag.String("addr", ":6080", "")
	flagDev := flag.Bool("dev", false, "")
	flagVer := flag.Bool("version", false, "Display version and exit")

	flag.Parse()

	if *flagVer {
		fmt.Printf("houndd v%s", getVersion())
		os.Exit(0)
	}

	var cfg config.Config
	if err := cfg.LoadFromFile(*flagConf); err != nil {
		panic(err)
	}

	//加载数据库
	mysql.InitDatabase()
	//mysql.DB.AutoMigrate(&ChihiroGitlabDetail{})

	var idx map[string]*searcher.Searcher
	idx = getConfigFromMysql(&cfg)
	// It's not safe to be killed during makeSearchers, so register the
	// shutdown signal here and defer processing it until we are ready.
	shutdownCh := registerShutdownSignal()
	// Start the web server on a background routine.
	ws := web.Start(&cfg, *flagAddr, *flagDev)

	handleShutdown(shutdownCh, idx)

	host := *flagAddr
	if strings.HasPrefix(host, ":") {
		host = "localhost" + host
	}
	//if *flagDev {
	//	err := runHttp(" :9090", false, &cfg, idx)
	//	if err != nil {
	//		fmt.Println("api启动失败")
	//	}
	//}
	if *flagDev {
		info_log.Printf("[DEV] starting webpack-dev-server at localhost:8080...")
		webpack := exec.Command("./node_modules/.bin/webpack-dev-server", "--mode", "development")
		webpack.Dir = basepath + "/../../"
		webpack.Stdout = os.Stdout
		webpack.Stderr = os.Stderr
		err := webpack.Start()
		if err != nil {
			error_log.Println(err)
		}
	}
	c := cron.New()
	c.AddFunc("0 0 0-23/2 * * *", func() {
		fmt.Println("定时任务执行")
		idx = nowHandleShutdown(idx, &cfg)
		panic(ws.ServeWithIndex(idx))
	})
	c.Start()
	info_log.Printf("项目运行在：http://%s\n", host)

	// Fully enable the web server now that we have indexes
	panic(ws.ServeWithIndex(idx))
}
